package com.yonsz.xiaoai.demo;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.yonsz.open.platform.client.OpenApiClient;
import com.yonsz.open.platform.model.Action;
import com.yonsz.open.platform.model.Device;
import com.yonsz.open.platform.model.House;
import com.yonsz.xiaoai.client.DefaultXiaoaiClient;
import com.yonsz.xiaoai.client.i.XiaoaiClient;
import com.yonsz.xiaoai.client.i.XiaoaiClientListener;
import com.yonsz.xiaoai.client.i.XiaoaiSession;
import com.yonsz.xiaoai.client.model.XiaoaiInfo;
import com.yonsz.xiaoai.model.ControlCmd;
import com.yonsz.xiaoai.model.RecvMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 离线控制demo
 * 1. 外网获取离线命令，并缓存，
 * 2. 搜索局域网获取目标设备ip
 * 3. 连接目标设备
 * 4. 发送空调命令
 * 5. 获取返回结果
 */
public class OfflineControlDemo {
    static Logger log = LoggerFactory.getLogger(OfflineControlDemo.class);
    // 要测试的小艾mac
    final static String mac = "F0FE6B914DBF";
    // 获取设备列表接口的accessToken
    final static String accessToken = "72ac298f-0892-4af6-8ffd-45da914a2a4b";
    public static void main(String[] args) throws Exception {
        // 通过接口获取离线命令
        // 获取到设备列表后，可以将其缓存下来，供离线调用
        List<Device> deviceList = getDeviceList();
        List<String> offlineCmds = getAirOfflineCmds(deviceList);

        // 创建局域网连接客户端
        XiaoaiClient client = new DefaultXiaoaiClient(new XiaoaiClientListener() {
            /**
             * 收到设备消息
             * @param uuid
             * @param macId
             * @param message
             */
            @Override
            public void onReceived(String uuid, String macId, RecvMessage message) {
                log.info("收到消息：macId:{},detail:{},original:{}",message.getMacId(),message.getDetail(),message.getOriginalRecvMsg());
            }

            /**
             * 小艾离线
             * @param uuid
             * @param macId
             */
            @Override
            public void onDisconnected(String uuid, String macId) {
                log.info("onDisconnected:"+uuid);
            }

            /**
             * 连接成功
             * @param session
             */
            @Override
            public void onConnected(XiaoaiSession session) {
                log.info("onConnected:"+session.getInfo().getUuid());
            }
        });

        // 局域网UDP广播搜索小艾
        CompletableFuture<List<XiaoaiInfo>> search = client.search(3000);
        // 同步获取小艾列表
        List<XiaoaiInfo> list = search.get();
        log.error("搜索设备结束：{}",JSONUtil.toJsonStr(list));

        //查找目标小艾
        list = list.stream().filter(item -> StrUtil.equals(item.getMacId(), mac)).collect(Collectors.toList());
        if(list.size() == 0){
            //TODO  由于UDP广播的不稳定性，不一定能一次性就能搜索到，实际使用时需要重试搜索
            throw new RuntimeException("没有搜索到目标设备,请重试搜索");
        }
        XiaoaiInfo xiaoaiInfo = list.get(0);
        //获取小艾IP
        String targetIp = xiaoaiInfo.getIp();
        log.error("连接小艾：{}",JSONUtil.toJsonStr(xiaoaiInfo));
        // 连接网关设备，端口固定为8899
        CompletableFuture<XiaoaiSession> future = client.connect(targetIp, 8899,mac);
        // 同步获取session
        XiaoaiSession session = future.get();
        log.error("连接成功：{}",session.getInfo().getUuid());
        Thread.sleep(2000); //刚连接成功后，需要等待2-3秒，接收完版本信息，才能发命令

        if(offlineCmds.size()>1){
            // 如果是多条命令的，全部发出去，无需等待返回
            // 一般是非存储红外命令，将红外码编码在命令中发送，所以数据量比较大
            for (String cmd : offlineCmds) {
                session.send(cmd);
            }
        }else{
            //单条控制命令
            String cmd = offlineCmds.get(0);
            ControlCmd request = new ControlCmd(mac,cmd)
                    .addParam("oper","power");//状态返回时原样带回的请求参数
            // 等待小艾返回，不设置则不等待小艾返回结果
            request.setWaitReturn(true);
            CompletableFuture<RecvMessage> send = session.send(request);
            log.error("发送命令：{}",cmd);

            //同步获取返回结果
            RecvMessage ret = send.get();
            if( ret.getException()!= null){
                log.error("控制失败：{}",ret.getException().getMessage());
                return;
            }
            log.error("控制成功：{}，detail：{},请求参数：{}",
                    ret.getOriginalRecvMsg(),
                    //包含返回命令的解析结果，detail不一定所有命令都有值
                    JSONUtil.toJsonStr(ret.getDetail()),
                    JSONUtil.toJsonStr(ret.getRequest().getRequestParams()));
        }
    }

    /**
     * 调用开放平台接口，获取设备列表
     * @return
     */
    public static List<Device> getDeviceList(){
        OpenApiClient client = new OpenApiClient(accessToken);
        //获取家庭列表
        List<House> houseList = client.getHouseList();

        // 选择一个家庭
        String houseId = houseList.get(2).getHouseId();
        // 获取家庭下的设备列表，带离线命令
        List<Device> deviceList = client.getDeviceList(houseId, true);
        return deviceList;
    }

    /**
     * 从设备列表中获取开空调的命令
     * @param deviceList
     * @return
     */
    public static List<String> getAirOfflineCmds(List<Device> deviceList){
        deviceList = deviceList.stream().filter(item -> StrUtil.equals(item.getDeviceType(), "air")).collect(Collectors.toList());

        Action open = deviceList.get(0).getActions().stream().filter(item -> StrUtil.equals(item.getOper(), "power")).findFirst().get();
        return open.getCmds();
    }
}
